package cheme.ProcessModel.Interpreter;

import java.beans.PropertyChangeEvent;
import java.util.Iterator;

import cheme.ProcessModel.IModelElement;
import cheme.ProcessModel.ModelEnv;
import cheme.ProcessModel.ParsingUtil;
import cheme.ProcessModel.Equations.Equation;
import cheme.ProcessModel.ProcessUnits.Streams.Tags.AbstractTagComponent;
import cheme.ProcessModel.Table.Field;

public final class PreParseRules
{
//	private static IRule tagComponentUniqueID = new IRule()
//	{
//		public RuleResult check(Object object)
//		{
//			RuleResult result = new IRule.Failure((IModelElement)object, "The element name is already in use.");
//			AbstractTagComponent component = (AbstractTagComponent)object;
//			
//			Interpreter interpreter = ModelEnv.getInstanceFor(component).getInterpreter();
//			
//			if(!interpreter.modelExists(component.getLabel()) || //name is new
//				component == interpreter.getModelData(component.getLabel()).getModel()) //name belongs to this element
//			{
//				result = new IRule.Success((IModelElement)object);
//			}
//			
//			return result;
//		}
//	};
	
	public static final IRule isModelElement = new IRule()
	{
		public RuleResult applyTo(IModelElement object)
		{
			RuleResult result = new IRule.Success(object);
			
			if(!(object instanceof IModelElement))
			{
				result = new IRule.Failure((IModelElement)object, "The object is not of model element type.");
			}
			
			return result;
		}
	};
	
	public static final IRule nonNull = new IRule()
	{
		public RuleResult applyTo(IModelElement object)
		{
			RuleResult result = new IRule.Success(object);
			
			if(null == object)
			{
				result = new IRule.Failure(object, "The is null.");
			}
			
			return result;
		}
	};
	
	public static final IRule degreesOfFreedomTest = new IRule()
	{
		public RuleResult applyTo(IModelElement object)
		{
			RuleResult result = new IRule.Success(object);
			
			if(object instanceof IModelElement)
			{
				int equationCount = 0;
				int unknownCount = 0;
				
				IModelElement root = (object).getRoot();
		
				/*
				 * Visit the interpreter's model data to count equations (Kludge!!!)
				 */
				
				Interpreter interp = ModelEnv.getInstanceFor(root).getInterpreter();
				Iterator<ModelElementData> eqItr =  interp.modelDataIterator();
				ModelElementData cur = null;
							
 				while(eqItr.hasNext())
				{
					cur = eqItr.next();
										
					if(cur.getModel() instanceof Equation && !((Equation)cur.getModel()).isInitial())
					{
						equationCount++;
					}
 					else if(cur.getModel() instanceof AbstractTagComponent && ((AbstractTagComponent)cur.getModel()).hasUnknownValue())
					{
						unknownCount++;
					}
				}
				
				if(equationCount > unknownCount)
				{
					result = new IRule.Warning(root, "The system may need additional stream components defined.");
				}
				else if(equationCount < unknownCount)
				{
					result = new IRule.Warning(root, "The system may need additional equations to relate unkown stream components.");
				}
			}
			
			return result;
		}
	};
	
	/*
	 * This should, logically, not be associated with the pre/post constraint mechanism since it is invoked strictly for it's side effects of
	 * hooking listeners to the relevant players in a component's interpretation... yet the mechanical mechinisms are exactly the same and I 
	 * am short on time. Note that a similiar mechanism, "post test hookup," could be implemented and ran after all test and parsing runs
	 */
	
	/*
	 * Here, the basic idea is, to add updaters for the successfully interpreted component so that when one of it's referrent changes,
	 * it can instigte a re-inpterpretation. Since this is ran post-parse as THE LAST TEST, it can take for granted that all constraints
	 * are met and simp[ly do the hooking bit with minimal tests.
	 */
	public static final IRule equationListensToRefs = new EquationIteratingRule()
	{
		@Override
		protected RuleResult tokenCheck(Field equationToken)
		{
			final String token = equationToken.getText().trim();
			
			if(!token.isEmpty() && !ParsingUtil.isOperator(token) && !ParsingUtil.isNumber(token) && interpreter.modelExists(token))
			{
				final ModelElementData data = interpreter.getModelData(token);
				
				if(null != data && null != data.getModel())
				{
					IUpdater updater = new IUpdater.Stub(interpreter, getEquation())
					{
						@Override
						public boolean allowPerformUpdate(PropertyChangeEvent evt)
						{
							/*
							 * This test ensures that an update of this model element 
							 * will only occur when another element to which this one 
							 * refers is altered.
							 */
							/*IModelElement tmpModel = (IModelElement)evt.getSource();
							IModelElement tmp2Model = data.getModel();
							if (evt.getSource() == data.getModel()){
								return (true);
							}
							else{
								return false;
							}*/
							return (true);
						}
						
						@Override
						public String toString()
						{
							return "An equaiton is listening to a referrent " + token + ".";
						}
					};
					
					interpreter.addUpdater(updater);
				}
			}
			else
			{
				
			}
			
			return new IRule.Success((IModelElement)this.getEquation());
		}

	};
	
	public static final IRule tagComponentListensToRefs = new TagComponentQuantityIteratingRule()
	{
		@Override
		protected RuleResult tokenCheck(String quantityToken)
		{
			final String dummy = quantityToken;
			
			if(!quantityToken.isEmpty() && !ParsingUtil.isOperator(quantityToken) && !ParsingUtil.isNumber(quantityToken) && interpreter.modelExists(quantityToken))
			{				
				final ModelElementData data = interpreter.getModelData(quantityToken);
				
				IUpdater updater = new IUpdater.Stub(interpreter, getComponent())
				{
					@Override
					public boolean allowPerformUpdate(PropertyChangeEvent evt)
					{
						/*
						 * This test ensures that an update of this model element 
						 * will only occur when another element to which this one 
						 * refers is altered.
						 */
						return (evt.getSource() == data.getModel());
					}
					
					@Override
					public String toString()
					{
						return getComponent().getLabel() + " is listening to the referrent " + dummy + ".";
					}
				};
				
				interpreter.addUpdater(updater);
			}
			
			return new IRule.Success((IModelElement)this.getComponent());
		}
	};
}
